#include<cstdlib>
#include<iostream>
#include <memory>
#include <string>
#include <list>
using namespace std;

class Observer
{
public:
	virtual void Update() = 0;
};

class Subject
{
public:
	Subject():mlObserver(){}
	virtual ~Subject(){
		mlObserver.clear();
	}
	void Attach(Observer* ipObserver)
	{
		if (ipObserver)
		{
			mlObserver.push_back(ipObserver);
		}
		
	}
	void Detach(Observer* ipObserver)
	{
		for(list<Observer*>::iterator it = mlObserver.begin();it != mlObserver.end();++it)
		{
			if (*it == ipObserver)
			{
				mlObserver.remove(*it);
			}
		}
		
	}
	void Notify()
	{
		for(list<Observer*>::iterator it = mlObserver.begin();it != mlObserver.end();++it)
		{
			(*it)->Update();
		}
	}
private:
	list<Observer*> mlObserver; 
};

//ConcreteSubject
class ConcreteSubject: public Subject
{
public:
	ConcreteSubject(string iName, string iState): mName(iName),mSubjectState(iState){}
	~ConcreteSubject(){}
	void setState(string iStateStr)
	{
		mSubjectState = iStateStr;
	}
	string getState()
	{
		return mSubjectState;
	}
private:
	string mName;
	string mSubjectState;
};
//Add other ConcreteSubject only requires add a sub class of Subject

//Observer1
class ConcereteObserver: public Observer
{
public:
	ConcereteObserver( string iName, string iState, ConcreteSubject* iConcreteSubject):mName(iName), mObjserverState(iState)
	{
		mConcreteSubject = iConcreteSubject;
	}
	void Update()
	{
		cout<<"Observer name: "<<mName<<"\t ObserverState: "<<mObjserverState<<"\t Subject state"<<mConcreteSubject->getState() << endl;
	}
private:
	string mName;
	string mObjserverState;
	ConcreteSubject* mConcreteSubject; 
};
//Add other observers. 

//The client
int main()
{
	//Create notifier and observer 
	ConcreteSubject lConcreteSubject("ConcreteSubject1", "SubjectState1");
	Observer *lpObserver = new ConcereteObserver("Observer1", "ObseverState1",&lConcreteSubject);
	Observer *lpObserver2 = new ConcereteObserver("Observer2", "ObseverState2",&lConcreteSubject);
	
	//attach observer to notifier 
	lConcreteSubject.Attach(lpObserver);
	lConcreteSubject.Attach(lpObserver2);

	//change subject status
	lConcreteSubject.setState("SubjectState2");
	lConcreteSubject.Notify();
	
	delete lpObserver;
	delete lpObserver2;
	getchar();
}
